package com.xxg.renrensearch.service;

import com.xxg.renrencommon.domain.bean.CarryProductInfo;
import com.xxg.renrensearch.baidu.ImageRetrieval;
import com.xxg.renrensearch.dao.*;
import com.xxg.renrensearch.domain.query.*;
import com.xxg.renrensearch.domain.result.*;
import com.xxg.renrensearch.es.RenRenShopEs;
import com.xxg.renrensearch.exception.ErrorMessage;
import com.xxg.renrensearch.init.RenrenShopProductsInit;
import com.xxg.renrensearch.repository.RenrenShopShopRepository;
import com.xxg.renrensearch.service.common.CommonService;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 商品接口实现
 *
 * @author xuxiaogang
 * @date 2021-11-26
 */
@Service
public class ProductServiceImpl implements ProductService {
    private final RenRenShopEs renRenShopEs;
    private final StatisticsService statisticsService;
    private final CarryProductInfoDao carryProductInfoDao;
    private final ProductGatherInfoDao productGatherInfoDao;
    private final RenrenShopShopRepository renrenShopShopRepository;
    private final CommonService commonService;
    private final ImageRetrieval imageRetrieval;
    private final QpsComponent qpsComponent;
    private final RenrenShopGoodsDao renrenShopGoodsDao;
    private final RenrenShopGoodsOptionDao renrenShopGoodsOptionDao;
    private final RenrenShopProductsInit renrenShopProductsInit;

    public ProductServiceImpl(RenRenShopEs renRenShopEs,
                              StatisticsService statisticsService, CarryProductInfoDao carryProductInfoDao, ProductGatherInfoDao productGatherInfoDao,
                              RenrenShopShopRepository renrenShopShopRepository, CommonService commonService,
                              ImageRetrieval imageRetrieval,
                              RenrenShopGoodsDao renrenShopGoodsDao, RenrenShopGoodsOptionDao renrenShopGoodsOptionDao,
                              RenrenShopProductsInit renrenShopProductsInit) {
        this.renRenShopEs = renRenShopEs;
        this.statisticsService = statisticsService;
        this.carryProductInfoDao = carryProductInfoDao;
        this.productGatherInfoDao = productGatherInfoDao;
        this.renrenShopShopRepository = renrenShopShopRepository;
        this.commonService = commonService;
        this.imageRetrieval = imageRetrieval;
        this.renrenShopGoodsDao = renrenShopGoodsDao;
        this.renrenShopGoodsOptionDao = renrenShopGoodsOptionDao;
        this.renrenShopProductsInit = renrenShopProductsInit;
        qpsComponent = new QpsComponent(2);
    }

    /**
     * 从数据库中查询商品信息，避免索引库中数据没同步好
     *
     * @param spuId 商品ID
     * @return 结果
     */
    private GoodWithOptions getGoodsAndOptions(Integer spuId) {
        GoodWithOptions goodWithOptions = new GoodWithOptions();
        RenrenShopGoods good = renrenShopGoodsDao.selectById(spuId);
        goodWithOptions.setGood(good);
        if (good != null) {
            if (good.getHasOption()) {
                // 查询规格
                List<RenrenShopGoodsOption> options = renrenShopGoodsOptionDao.selectByGoodId(spuId);
                goodWithOptions.setOptions(options);
            }
        }
        return goodWithOptions;
    }

    @Override
    public BaseResult<List<Product>> findProductBySpuId(Integer spuId) {
        GoodWithOptions goods = getGoodsAndOptions(spuId);
        // 商品不存在
        if (goods.getGood() == null) {
            // 从es中删除
            ArrayList<Integer> spuIds = new ArrayList<>();
            spuIds.add(spuId);
            renRenShopEs.deleteAllByIds(Product.class, "spuId", spuIds);
            return new BaseResult<>(new ArrayList<>(0));
        }
        // 查询带货关系，找到原始商品，修改库存
        CarryProductInfo info = carryProductInfoDao.findOriSpuIdByCarrySpuId(spuId);
        if (info != null) {
            // 从数据库中查询原商品库存信息
            GoodWithOptions oriGoods = getGoodsAndOptions(info.getSpuId());
            // 原商品不存在
            if (oriGoods.getGood() == null) {
                return new BaseResult<>(new ArrayList<>(0));
            }
            // 原商品无规格
            if (oriGoods.getOptions() == null || oriGoods.getOptions().size() == 0) {
                goods.getGood().setStock(oriGoods.getGood().getStock());
                return new BaseResult<>(Collections.singletonList(renrenShopProductsInit.good2Product(goods.getGood())));
            }
            // 带货商品无规格，则此商品视为不存在
            if (goods.getOptions().size() == 0) {
                return new BaseResult<>(new ArrayList<>(0));
            }
            // 将原商品信息中库存同步到带货的商品信息中
            Map<String, Integer> newOldSpuMap = info.getSkuIdMap();
            Map<Integer, Integer> oldStockMap = new HashMap<>(oriGoods.getOptions().size() * 4 / 3 + 1);
            oriGoods.getOptions().forEach(e -> oldStockMap.put(e.getId(), e.getStock()));
            List<RenrenShopGoodsOption> options = new ArrayList<>(goods.getOptions().size());
            goods.getOptions().forEach(e -> {
                Integer oriOptionsId = newOldSpuMap.get(e.getId().toString());
                if (oriOptionsId != null) {
                    Integer stock = oldStockMap.get(oriOptionsId);
                    if (stock != null) {
                        e.setStock(stock);
                        options.add(e);
                    }
                } else {
                    options.add(e);
                }
            });
            goods.setOptions(options);
            // 发现带货规格与原始规格没有匹配上，即视为商品不存在
            if (options.size() == 0) {
                return new BaseResult<>(new ArrayList<>(0));
            }
            return new BaseResult<>(renrenShopProductsInit.goodOption2Product(goods.getGood(), goods.getOptions()));
        } else {
            // 带货商品无规格
            if (goods.getOptions() == null || goods.getOptions().size() == 0) {
                return new BaseResult<>(Collections.singletonList(renrenShopProductsInit.good2Product(goods.getGood())));
            } else {
                return new BaseResult<>(renrenShopProductsInit.goodOption2Product(goods.getGood(), goods.getOptions()));
            }
        }
    }

    @Override
    public BaseResult<List<Product>> findOrderedProductByShopId(GetProductByOrderTypeQuery query) {
        Integer carryAuth = query.getCarryAuth();
        Integer carryStatus = query.getCarryStatus();
        if (carryAuth != null && carryStatus != null) {
            return new BaseResult<>(ReturnCodeEnum.INVALID_PARAMETER, ErrorMessage.CANNOT_BOTH_VALUED, null);
        }
        // 根据带货权限查询商品spuId列表
        List<Integer> spuIds = getSpuIdByShopIdAndCarryAuth(query.getShopId(), carryAuth);
        // 查询已带货的spuId列表
        List<Integer> carriedSpuIds;
        if (carryStatus != null) {
            carriedSpuIds = statisticsService.getCarriedSpuIdByShopIdAndOriShopId(query.getMyShopId(),
                    query.getShopId());
            spuIds.addAll(carriedSpuIds);
        }
        String[] requiredFields = {"id", "skuName", "spuThumb", "skuPrice", "shopName"};
        Pageable pageable = PageRequest.of(query.getPageNo() - 1, 10);
        String orderField = "sales";
        if (query.getOrderType() == 1) {
            orderField = "skuPrice";
        }
        SortOrder sortOrder = SortOrder.ASC;
        if (query.getSortType() == 1) {
            sortOrder = SortOrder.DESC;
        }
        if ("sales".equals(orderField)) {
            sortOrder = SortOrder.DESC;
        }
        List<Product> products = renRenShopEs.findByShopId(query.getShopId(), pageable, requiredFields, orderField,
                sortOrder, query.getCategories(), spuIds);
        return new BaseResult<>(products);
    }

    @Override
    public BaseResult<List<Product>> findProductByKeyword(GetProductByKeyWordQuery query) {
        String[] requiredFields = {"id", "skuName", "spuThumb", "skuPrice", "shopName", "sales"};
        Pageable pageable = PageRequest.of(query.getPageNo() - 1, 10);
        List<Product> products = renRenShopEs.findByKeyword(query.getShopId(), pageable, requiredFields,
                query.getKeyword(), query.getCategories());
        return new BaseResult<>(products);
    }

    /**
     * 根据店铺和带货权限查询SPUID列表
     *
     * @param shopId    店铺ID
     * @param carryAuth 带货权限
     * @return 结果
     */
    private List<Integer> getSpuIdByShopIdAndCarryAuth(Integer shopId, Integer carryAuth) {
        List<Integer> spuIds = new LinkedList<>();
        if (carryAuth != null) {
            spuIds = statisticsService.getShopCarryTypeSpuCountById(shopId, carryAuth);
        } else {
            spuIds.add(1);
        }
        return spuIds;
    }

    @Override
    public BaseResult<List<Product>> findMyOrderedProduct(GetMyProductQuery query) {
        Integer carryAuth = query.getCarryAuth();
        if (carryAuth != null && query.getIsGather()) {
            return new BaseResult<>(ReturnCodeEnum.INVALID_PARAMETER, ErrorMessage.CANNOT_BOTH_VALUED2, null);
        }
        // 根据带货权限查询商品spuId列表
        List<Integer> spuIds = getSpuIdByShopIdAndCarryAuth(query.getShopId(), carryAuth);
        if (query.getIsGather()) {
            List<Integer> newSpuIds = statisticsService.getGatherSpuIdByShopId(query.getShopId());
            spuIds.addAll(newSpuIds);
        }
        String[] requiredFields = {"id", "skuName", "spuThumb", "skuPrice", "shopName"};
        Pageable pageable = PageRequest.of(query.getPageNo() - 1, 10);
        String orderField = "sales";
        if (query.getOrderType() == 1) {
            orderField = "skuPrice";
        }
        SortOrder sortOrder = SortOrder.ASC;
        if (query.getSortType() == 1) {
            sortOrder = SortOrder.DESC;
        }
        if ("sales".equals(orderField)) {
            sortOrder = SortOrder.DESC;
        }
        List<Product> products = renRenShopEs.findByShopId(query.getShopId(), pageable, requiredFields, orderField,
                sortOrder, query.getCategories(), spuIds);
        return new BaseResult<>(products);
    }

    @Override
    public BaseResult<ProductCountsResult> findProductCountsByShopId(int shopId) {
        // 获取自己的spu数量
        int myCount = renRenShopEs.getShopSpuCount(shopId);
        // 获取已带货、被带货的spu数量
        Integer[] counts = statisticsService.getCarriedBeCarriedCounts(shopId);
        ProductCountsResult productCountsResult = new ProductCountsResult(myCount, counts[0], counts[1]);
        return new BaseResult<>(productCountsResult);
    }

    @Override
    public BaseResult<List<ShopWithProducts>> findCarriedProductGroupByShop(GetCarriedProductQuery query) {
        List<CarriedSpuResult> list = carryProductInfoDao.findCarriedSpusGroupByShop(query.getShopId(),
                query.getPageNo());
        List<Integer> shopIds = new ArrayList<>(list.size());
        List<Integer> spuIds = new ArrayList<>(3 * list.size());
        for (CarriedSpuResult carriedSpuResult : list) {
            shopIds.add(carriedSpuResult.getShopId());
            int size = carriedSpuResult.getSpuIds().size();
            int toIndex = Math.min(size, 3);
            spuIds.addAll(carriedSpuResult.getSpuIds().subList(0, toIndex));
        }
        // 查询店铺信息
        Iterable<RenrenShopShop> shops = renrenShopShopRepository.findAllById(shopIds);
        Map<Integer, RenrenShopShop> shopMap = new HashMap<>(14);
        shops.forEach(e -> shopMap.put(e.getId(), e));
        // 查询商品信息
        String[] requiredFields = {"id", "shopId", "skuName", "spuThumb", "skuPrice"};
        List<Product> products = renRenShopEs.findCollapseSkusBySpuIds(spuIds, requiredFields);
        Map<Integer, List<Product>> productMap = new HashMap<>(14);
        products.forEach(e -> {
            if (!productMap.containsKey(e.getShopId())) {
                productMap.put(e.getShopId(), new ArrayList<>(3));
            }
            List<Product> products1 = productMap.get(e.getShopId());
            if (products1.size() < 3) {
                products1.add(e);
            }
        });
        // 组装结果
        List<ShopWithProducts> shopWithProducts = new ArrayList<>(list.size());
        for (CarriedSpuResult e : list) {
            RenrenShopShop shop = shopMap.get(e.getShopId());
            List<Product> goods = productMap.get(e.getShopId());
            ShopWithProducts shopWithProduct = new ShopWithProducts(shop, goods);
            shopWithProducts.add(shopWithProduct);
        }
        commonService.extraInfoProcess(query.getShopId(), shopWithProducts);
        return new BaseResult<>(shopWithProducts);
    }

    @Override
    public BaseResult<List<ShopWithProducts>> findBeCarriedProductGroupByShop(GetCarriedProductQuery query) {
        List<CarriedSpuResult> list = carryProductInfoDao.findBeCarriedSpusGroupByShop(query.getShopId(),
                query.getPageNo());
        List<Integer> shopIds = new ArrayList<>(list.size());
        List<Integer> spuIds = new ArrayList<>(3 * list.size());
        for (CarriedSpuResult carriedSpuResult : list) {
            shopIds.add(carriedSpuResult.getShopId());
            int size = carriedSpuResult.getSpuIds().size();
            int toIndex = Math.min(size, 3);
            spuIds.addAll(carriedSpuResult.getSpuIds().subList(0, toIndex));
        }
        // 查询店铺信息
        Iterable<RenrenShopShop> shops = renrenShopShopRepository.findAllById(shopIds);
        Map<Integer, RenrenShopShop> shopMap = new HashMap<>(14);
        shops.forEach(e -> shopMap.put(e.getId(), e));
        // 查询商品信息
        String[] requiredFields = {"id", "shopId", "skuName", "spuThumb", "skuPrice"};
        List<Product> products = renRenShopEs.findCollapseSkusBySpuIds(spuIds, requiredFields);
        Map<Integer, List<Product>> productMap = new HashMap<>(14);
        Map<Integer, Integer> beCarriedCountMap = new HashMap<>(14);
        products.forEach(e -> {
            if (!productMap.containsKey(e.getShopId())) {
                productMap.put(e.getShopId(), new ArrayList<>(3));
                beCarriedCountMap.put(e.getShopId(), 0);
            }
            int count = beCarriedCountMap.get(e.getShopId());
            beCarriedCountMap.put(e.getShopId(), count + 1);
            List<Product> products1 = productMap.get(e.getShopId());
            if (products1.size() < 3) {
                products1.add(e);
            }
        });
        // 组装结果
        List<ShopWithProducts> shopWithProducts = new ArrayList<>(list.size());
        for (CarriedSpuResult e : list) {
            RenrenShopShop shop = shopMap.get(e.getShopId());
            List<Product> goods = productMap.get(e.getShopId());
            ShopWithProducts shopWithProduct = new ShopWithProducts(shop, goods);
            shopWithProducts.add(shopWithProduct);
        }
        commonService.extraInfoProcess(query.getShopId(), shopWithProducts);
        // 设置被带货数量
        shopWithProducts.forEach(e -> e.getExtraInfo().setBeCarriedCount(beCarriedCountMap.getOrDefault(e.getShop().getId(),0)));
        return new BaseResult<>(shopWithProducts);
    }

    @Override
    public BaseResult<List<Product>> findMyCarriedProductByKeyword(GetProductByKeyWordQuery2 query) {
        List<CarryProductInfo> infos = carryProductInfoDao.findCarriedSpusByShopId(query.getShopId(), query.getPageNo());
        return findProductByKeyword(infos, query);
    }

    @Override
    public BaseResult<List<Product>> findMyBeCarriedProductByKeyword(GetProductByKeyWordQuery2 query) {
        List<CarryProductInfo> infos = carryProductInfoDao.findBeCarriedSpusByShopId(query.getShopId(), query.getPageNo());
        return findProductByKeyword(infos, query);
    }

    @Override
    public BaseResult<List<Product>> findMyCarriedProductByShop(GetCarriedProductQuery2 query) {
        List<CarryProductInfo> infos = carryProductInfoDao.findCarriedSpusByShopId2(query.getShopId(),
                query.getDestShopId(), query.getPageNo());
        return findProductBySpuIds(infos);
    }

    @Override
    public BaseResult<List<Product>> findBeMyCarriedProductByShop(GetCarriedProductQuery2 query) {
        List<CarryProductInfo> infos = carryProductInfoDao.findCarriedSpusByShopId2(query.getDestShopId(),
                query.getShopId(), query.getPageNo());
        return findProductBySpuIds(infos);
    }

    @Override
    public BaseResult<List<Product>> searchByPic(byte[] file) {
        if (!qpsComponent.tryAcquireQps(3000)) {
            return new BaseResult<>(ReturnCodeEnum.SERVER_BUSY, ErrorMessage.SERVER_BUSY, null);
        }
        List<Map<String, String>> images = imageRetrieval.searchByPic(file);
        List<Product> products = renRenShopEs.multiQueryProduct(images);
        return new BaseResult<>(products);
    }

    @Override
    public BaseResult<ProductCountsBySpuResult> findProductCountsBySpu(Integer spuId) {
        long beCarriedCount = carryProductInfoDao.countBeCarriedBySpuId(spuId);
        long beGatherCount = productGatherInfoDao.countBeGatherBySpuId(spuId);
        ProductCountsBySpuResult result = new ProductCountsBySpuResult(beCarriedCount, beGatherCount);
        return new BaseResult<>(result);
    }

    private BaseResult<List<Product>> findProductByKeyword(List<CarryProductInfo> infos, GetProductByKeyWordQuery2 query) {
        List<Integer> spuIds = infos.stream().map(CarryProductInfo::getSpuId).collect(Collectors.toList());
        // 模糊查询
        Pageable pageable = PageRequest.of(query.getPageNo() - 1, 10);
        String[] fields = {"id", "shopId", "skuName", "spuThumb", "skuPrice", "shopName"};
        List<Product> products = renRenShopEs.findByKeyword(spuIds, pageable, fields, query.getKeyword());
        return new BaseResult<>(products);
    }

    private BaseResult<List<Product>> findProductBySpuIds(List<CarryProductInfo> infos) {
        List<Integer> spuIds = infos.stream().map(CarryProductInfo::getSpuId).collect(Collectors.toList());
        String[] fields = {"id", "shopId", "skuName", "spuThumb", "skuPrice", "shopName"};
        List<Product> products = renRenShopEs.findCollapseSkusBySpuIds(spuIds, fields);
        return new BaseResult<>(products);
    }
}
